//
//  CollectionViewComponent.swift
//  ClockWidget
//
//  Created by TSYH on 2023/7/18.
//

import UIKit

open class CollectionView: UICollectionView {
    
}

open class CollectionViewComponent: NSObject {
    public var itemDidSelectedHandler: ((Any?, IndexPath) -> Void)?
    public var itemActionHandler: ((Any?, IndexPath) -> Void)?
    
    /// cellData, parameters, indexPath
    public var innerActionHandler: ((Any?, Any?, IndexPath?) -> Void)?
    
    public var itemWillDisplayHandler: ((UICollectionViewCell, Any?, IndexPath) -> Void)?
    public var itemDidEndDisplayingHandler: ((UICollectionViewCell, Any?, IndexPath) -> Void)?
    
    public var didScrollHandler: ((UIScrollView) -> Void)?
    public var willBeginDraggingHandler: ((UIScrollView) -> Void)?
    public var didEndDraggingHandler: ((UIScrollView, Bool) -> Void)?
    public var didScrollToTopHandler: ((UIScrollView) -> Void)?
    public var willEndDraggingWithVelocityHandler: ((UIScrollView, CGPoint) -> Void)?
    public var didEndDeceleratingHandler: ((UIScrollView) -> Void)?
    
    public var collectionView: CollectionView!
    var attributes: [String: Any]?
    
    static let defaultCellID: String = "Component_Default_Cell_ID"
    public init(frame: CGRect, layout: UICollectionViewFlowLayout? = nil, attributes: [String: Any]? = nil) {
        super.init()
        
        var clLayout = UICollectionViewFlowLayout()
        if let direction = attributes?["direction"] as? UICollectionView.ScrollDirection {
            clLayout.scrollDirection = direction
        }
        
        if let layout = layout {
            clLayout = layout
        }
        collectionView = CollectionView(frame: frame, collectionViewLayout: clLayout)
        collectionView.delegate = self
        collectionView.dataSource = self
        self.attributes = attributes
        if let showScrollIndicator = attributes?["showScrollIndicator"] as? Bool {
            collectionView.showsHorizontalScrollIndicator = showScrollIndicator
        }
        
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: Self.defaultCellID)
    }
    
    public var view: UICollectionView {
        return collectionView
    }
    
    lazy var dataSource = [Component]()
    public func clear() {
        dataSource = []
    }
    
    public func reloadView(with components: [Component]) {
        registerClass(components: components)
        dataSource.append(contentsOf: components)
        collectionView.reloadData()
    }
    
    public func reloadData() {
        collectionView.reloadData()
    }
    
    public func add(sections: [CollectionViewSectionComponent]) {
        dataSource.append(contentsOf: sections)
    }
    
    // 添加子元素
    public func append(childrens: [Component], inset: UIEdgeInsets = .zero, rowSpace: CGFloat = 0, columnSpace: CGFloat = 0) {
        registerClass(components: childrens)
        
        var lastSection: CommonSectionComponent? = dataSource.first(where: { $0 is CommonSectionComponent }) as? CommonSectionComponent
        if lastSection == nil {
            lastSection = CommonSectionComponent(cellComponents: [], inset: inset, rowSpacing: rowSpace, columnSpacing: columnSpace)
        }
        childrens.forEach { child in
            lastSection?.append([child])
        }
        if dataSource.isEmpty, let lastSection = lastSection {
            dataSource.append(lastSection)
        }
    }
    
    // 注册cell
    private func registerClass(components: [Component]) {
        for cp in components {
            if let sectionCp = cp as? CollectionViewSectionComponent {
                if let header = sectionCp.headerComponent {
                    collectionView.register(header.viewClass, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: header.viewClass.description())
                }
                if let footer = sectionCp.footerComponent {
                    collectionView.register(footer.viewClass, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: footer.viewClass.description())
                }
                for cellCp in sectionCp.cells {
                    collectionView.register(cellCp.cellClass, forCellWithReuseIdentifier: cellCp.cellClass.description())
                }
            } else if let cellCp = cp as? CollectionViewCellComponent {
                collectionView.register(cellCp.cellClass, forCellWithReuseIdentifier: cellCp.cellClass.description())
            }
        }
    }
}

extension CollectionViewComponent {
    private var isSectionFramework: Bool {
        if dataSource.first is CollectionViewSectionComponent {
            return true
        }
        return false
    }
    
    func cellComponent(at indexPath: IndexPath) -> CollectionViewCellComponent? {
        var cellCp: Component?
        if isSectionFramework {
            if indexPath.section < dataSource.count,
                let sectionCp = dataSource[indexPath.section] as? CollectionViewSectionComponent,
                indexPath.item < sectionCp.cells.count {
                cellCp = sectionCp.cells[indexPath.item]
            }
        } else if indexPath.item < dataSource.count {
            cellCp = dataSource[indexPath.item]
        }
        if let cellCp = cellCp as? CollectionViewCellComponent {
            return cellCp
        }
        return nil
    }
}

extension CollectionViewComponent: UICollectionViewDataSource {
    
    public func numberOfSections(in collectionView: UICollectionView) -> Int {
        if isSectionFramework {
            return dataSource.count
        }
        return 1
    }
    
    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if isSectionFramework {
            if section < dataSource.count {
                return (dataSource[section] as? CollectionViewSectionComponent)?.cells.count ?? 0
            }
            return 0
        }
        return dataSource.count
    }
    
    public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        itemWillDisplayHandler?(cell, cellComponent(at: indexPath), indexPath)
    }
    
    public func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        itemDidEndDisplayingHandler?(cell, cellComponent(at: indexPath), indexPath)
    }
    
    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cellCp = cellComponent(at: indexPath) else {
            return collectionView.dequeueReusableCell(withReuseIdentifier: Self.defaultCellID, for: indexPath)
        }
        
        let cellClass = cellCp.cellClass
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellClass.description(), for: indexPath)
        if let swipeCell = cell as? SwipeCollectionCell, let _ = cellCp as? CollectionViewCellSwipeComponent {
            swipeCell.swipDataSource = self
        }
        
        if let cell = cell as? ComponentView {
            var atts = attributes ?? [:]
            atts["indexPath"] = indexPath
            cell.renderView(with: cellCp, component: self, attributes: atts)
        }
        return cell
    }
    
    public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        guard let sectionCp = dataSource[indexPath.section] as? CollectionViewSectionComponent else {
            return UICollectionReusableView()
        }
        if kind == UICollectionView.elementKindSectionHeader {
            if let headerCp = sectionCp.headerComponent {
                let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerCp.viewClass.description(), for: indexPath)
                if let headerView = header as? ComponentView {
                    headerView.renderView(with: headerCp, component: self, attributes: attributes)
                }
                return header
            }
        } else if kind == UICollectionView.elementKindSectionFooter {
            if let footerCp = sectionCp.headerComponent {
                let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerCp.viewClass.description(), for: indexPath)
                if let footerView = footer as? ComponentView {
                    footerView.renderView(with: footerCp, component: self, attributes: attributes)
                }
                return footer
            }
        }
        return UICollectionReusableView()
    }
}

extension CollectionViewComponent: UICollectionViewDelegateFlowLayout {
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        guard let cellCp = cellComponent(at: indexPath) else {
            return .zero
        }
        return cellCp.cellSize(with: attributes, in: collectionView.frame)
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        if isSectionFramework {
            if section < dataSource.count {
                return (dataSource[section] as? CollectionViewSectionComponent)?.sectionInset ?? .zero
            }
        }
        return UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16)
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        if section < dataSource.count {
            return (dataSource[section] as? CollectionViewSectionComponent)?.lineSpacing ?? 12.0
        }
        return 12.0
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        if section < dataSource.count {
            return (dataSource[section] as? CollectionViewSectionComponent)?.itemSpacing ?? 12.0
        }
        return 12.0
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
        if isSectionFramework {
            if section < dataSource.count,
                let sectionCp = dataSource[section] as? CollectionViewSectionComponent {
                return sectionCp.footerComponent?.viewSize ?? .zero
            }
        }
        return .zero
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        if isSectionFramework {
            if section < dataSource.count,
                let sectionCp = dataSource[section] as? CollectionViewSectionComponent {
                return sectionCp.headerComponent?.viewSize ?? .zero
            }
        }
        return .zero
    }
}

extension CollectionViewComponent: UICollectionViewDelegate {
    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        itemDidSelectedHandler?(cellComponent(at: indexPath), indexPath)
    }
    
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        didScrollHandler?(scrollView)
    }
    
    public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        willEndDraggingWithVelocityHandler?(scrollView, velocity)
    }
    
    public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        willBeginDraggingHandler?(scrollView)
    }
    
    public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        didEndDraggingHandler?(scrollView, decelerate)
    }
    
    public func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
        didScrollToTopHandler?(scrollView)
    }
    
    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        didEndDeceleratingHandler?(scrollView)
    }
}

// 侧滑菜单
extension CollectionViewComponent: SwipeCollectionCellDataSource {
    public func swipeEnable(for collectionCell: SwipeCollectionCell) -> Bool {
        // 重置所有
        collectionView.visibleCells.forEach { cell in
            if let swipeCell = cell as? SwipeCollectionCell, swipeCell != collectionCell {
                swipeCell.resetState()
            }
        }
        
        guard let indexPath = collectionView.indexPath(for: collectionCell),
              let cellCp = cellComponent(at: indexPath) as? CollectionViewCellSwipeComponent,
              let actions = cellCp.swipeActions else {
            return false
        }
        return !actions.isEmpty
    }
    
    public func swipeActions(for collectionCell: SwipeCollectionCell) -> [SwipeAction]? {
        guard let indexPath = collectionView.indexPath(for: collectionCell),
              let cellCp = cellComponent(at: indexPath) as? CollectionViewCellSwipeComponent,
              let actions = cellCp.swipeActions else {
            return nil
        }
        return actions
    }
    
    public func swipeCell(_ collectionCell: SwipeCollectionCell, didSelected action: SwipeAction) {
        guard let indexPath = collectionView.indexPath(for: collectionCell),
              let cellCp = cellComponent(at: indexPath) else {
            return
        }
        innerActionHandler?(cellCp, action, indexPath)
    }
}
